#define _GNU_SOURCE
#include "cmd_ls.h"
#include "stdio.h"
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#define DEBUG

// ls -l test
int cmd_ls_execute(cmd_t *pcmd)
{
    if (pcmd == NULL)
    {
        return -1;
    }

    if (pcmd->cmd_arg_count != 2)
    {
        fprintf(stderr, "Command argument Error\n");
    }

    if (pcmd->cmd_arg_list[1] != NULL)
    {
        // cmd_arg_list[1]为文件路径
        return cmd_list_directory(pcmd->cmd_arg_list[1]);
    }
    else
    {
        return -1;
    }
}

// 目录遍历 test 下有 text1 text2 subdir
int cmd_list_directory(char *dirpath)
{
    DIR *pdir = NULL;
    struct dirent *pdirent = NULL;
    file_attribute_t attr;
    char path[128] = {0};

    pdir = opendir(dirpath);
    if (pdir == NULL)
    {
        perror("[ERROR] opendir():");
        return -1;
    }

    while ((pdirent = readdir(pdir)) != NULL)
    {
        /* code */
        if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0)
        {
            continue;
        }
#ifdef DEBUG
        printf("[DEBUG] filename: %s\n", pdirent->d_name);
#endif
        memset(&attr, 0, sizeof(attr));
        // 合成目录下的文件
        make_path_ls(path, dirpath, pdirent->d_name);

        strcpy(attr.f_attr_name, pdirent->d_name);

        // 判断是否为链接文件
        if (pdirent->d_type == DT_LNK)
        {
            get_file_attr(&attr, path, pdirent->d_name, true);
        }
        else
        {
            get_file_attr(&attr, path, pdirent->d_name, false);
        }

        // 显示文件属性
        show_file_attributes(&attr);
    }
    closedir(pdir);
}

// test/text1    test/text2     test/subdir
void make_path_ls(char *path, char *dirpath, char *filename)
{
    strcpy(path, dirpath);
    strcat(path, "/");
    strcat(path, filename);
}

// 获取属性
int get_file_attr(struct file_attribute *pattr, const char *path, const char *name, bool islink)
{
    int ret;

    // 判断是否为链接文件（软链接）
    if (islink)
    {
        ret = lstat(path, &pattr->f_attr_stat_info);
    }
    else
    {
        ret = stat(path, &pattr->f_attr_stat_info);
    }

    if (ret == -1)
    {
        perror("stat():");
        return -1;
    }

    // 获取文件类型
    get_file_type_ls(pattr);

    if (pattr->f_attr_type == 'l')
    {
        int ret = readlink(path, pattr->f_attr_link_content, sizeof(pattr->f_attr_link_content));
        if (ret == -1)
        {
            perror("readlink():");
            return -1;
        }
    }

    // 获取文件权限
    get_file_permission(pattr);
    // 获取用户名
    get_file_uname(pattr);
    // 获取用户组
    get_file_gname(pattr);
    get_file_last_modify_time(pattr);

    return 0;
}

// 打印文件属性 文件类型
void show_file_attributes(struct file_attribute *pattr)
{
    printf("%c ", pattr->f_attr_type);
    printf("%s ", pattr->f_attr_permission);
    // 文件硬连接的次数
    printf("%ld ", pattr->f_attr_stat_info.st_nlink);
    printf("%s ", pattr->f_attr_uname);
    printf("%s ", pattr->f_attr_gname);
    // 文件大小
    printf("%ld ", pattr->f_attr_stat_info.st_size);
    printf("%s ", pattr->f_attr_mtime);

    // 链接文件名
    if (pattr->f_attr_type == 'l')
    {
        printf("%s -> %s\n", pattr->f_attr_name, pattr->f_attr_link_content);
    }
    else
    {
        printf("%s\n", pattr->f_attr_name);
    }

    putchar('\n');
}

// 获取文件类型 将信息保存到结构体成员
int get_file_type_ls(struct file_attribute *pattr)
{
    mode_t mode = pattr->f_attr_stat_info.st_mode;

    if (pattr == NULL)
    {
        return -1;
    }

    switch (mode & __S_IFMT)
    {
    case __S_IFBLK:
        // 块设备
        pattr->f_attr_type = 'b';
        break;
    case __S_IFCHR:
        // 字符设备
        pattr->f_attr_type = 'c';
        break;
    case __S_IFDIR:
        // 目录文件
        pattr->f_attr_type = 'd';
        break;
    case __S_IFIFO:
        // 管道
        pattr->f_attr_type = 'p';
        break;
    case __S_IFLNK:
        // 符号链接文件
        pattr->f_attr_type = 'l';
        break;
    case __S_IFREG:
        // 普通文件
        pattr->f_attr_type = '-';
        break;
    case __S_IFSOCK:
        // 套接字
        pattr->f_attr_type = 's';
        break;
    default:
        break;
    }

    return 0;
}

// 获取文件权限
int get_file_permission(struct file_attribute *pattr)
{
    mode_t mode = pattr->f_attr_stat_info.st_mode;
    int index = 0;
    char perm[] = {'r', 'w', 'x'};

    for (int i = 8; i >= 0; i--)
    {
        if ((mode >> i) & 0x1)
        {
            pattr->f_attr_permission[index] = perm[index % 3];
        }
        else
        {
            pattr->f_attr_permission[index] = '-';
        }

        index++;
    }

    pattr->f_attr_permission[index] = '\0';
    return 0;
}

// 获取用户属主
void get_file_uname(struct file_attribute *pattr)
{
    struct passwd *pwd = getpwuid(pattr->f_attr_stat_info.st_uid);
    strcpy(pattr->f_attr_uname, pwd->pw_name);
}

// 获取用户组
void get_file_gname(struct file_attribute *pattr)
{
    struct group *grp = getgrgid(pattr->f_attr_stat_info.st_gid);
    strcpy(pattr->f_attr_gname, grp->gr_name);
}

// 获取最后修改时间
void get_file_last_modify_time(struct file_attribute *pattr)
{
    char *timestr = ctime(&pattr->f_attr_stat_info.st_mtime);
    strcpy(pattr->f_attr_mtime, timestr);
    pattr->f_attr_mtime[strlen(timestr) - 1] = '\0';
}